#include "calibration.h"

#include <QDebug>

#define THRESHOLD_DELTA 0.2

Calibration::Calibration(QWidget *parent) : QWizard(parent)
{
    introPage = new IntroCalPage();
    calPage = new CalibrationPage();

    addPage(introPage);
    addPage(calPage);
}


void Calibration::accept() {

    //config->calculateCalibration();

    QDialog::accept();
}

void Calibration::setConfig(Config *conf) {
    config = conf;
    calPage->setConfig(config);
}


IntroCalPage::IntroCalPage(QWidget *parent)
    : QWizardPage(parent)
{
    setTitle(tr("Introduction"));
    setSubTitle(tr("Welcome to Bimos Pro calibration system"));
    ////setpixmap(QWizard::WatermarkPixmap, QPixmap(":/images/watermark1.png"));

    label = new QLabel(tr("Calibration Text blabla."));
    label->setWordWrap(true);

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(label);
    setLayout(layout);
}

CalibrationPage::CalibrationPage(QWidget *parent)
    : QWizardPage(parent)
{
    // Two layouts to show radiobuttons and progressbar
    QVBoxLayout *vertLayout = new QVBoxLayout;
    QGridLayout *layout = new QGridLayout;
    QGridLayout *layout2 = new QGridLayout;

    /** initialize GUI and variables */

    /** init progress bar */
    pBar = new QProgressBar();
    pBar->setRange(0,500);
    pBar->repaint();

    /** set layouting */
    vertLayout->addWidget(pBar,    1, 0);
    vertLayout->addLayout(layout);
        vertLayout->addLayout(layout2);

    setLayout(vertLayout);
}

double CalibrationPage::showAskRefMbarDialog(bool *ok){
    return QInputDialog::getDouble(this, tr("Calibration Dialog"),
                                       tr("Please type in reference pressure (mbar)"), 1000, 0, 5000, 1, ok);
}

bool CalibrationPage::showAskKeepAbortCalibrationDialog(bool *ok){
    return ok;
}

//define DEFAULT_SLOPE 0.47762
//define DEFAULT_INTERCEPT 0.12957
void CalibrationPage::calculateCalibration(BimosSensorPressure *sensor){
    sensor->slope = (ref_mbar_upper - ref_mbar_lower)/(calibrate_upper - calibrate_lower);
    sensor->intercept = ref_mbar_lower - ((sensor->slope) * calibrate_lower);


    qDebug() << sensor->slope << sensor->intercept;

    // to get [bar] from [mbar]
    //calibrationMultiplicator[i] /= 1000;
    //calibrationOffset[i] /= 1000;
}

bool CalibrationPage::calibrate(double value){

    // skip identical value
    if (average == value)
        return true;

    double old_avg = average;
    average += value;

    double tmp_old = (old_avg/avg_count);
    double tmp_new = (average/++avg_count);



    qDebug() << "Old AVG: " << tmp_old << "New AVG: " << tmp_new << "DELTA: " << fabs(tmp_old - tmp_new);

    if (( fabs(tmp_old - tmp_new)  < DELTA) || (avg_count >= MAX_AVG_COUNT)){
        if (lowerCalibrationDone){
            upperCalibrationDone = true;
            calibrate_upper = tmp_new;
        } else {
            lowerCalibrationDone = true;
            calibrate_lower = tmp_new;
            // ask user to input reference pressure
            userInputMarker = true;
        }
    }
    return !(avg_count >= MAX_AVG_COUNT);
}

void CalibrationPage::abortCalibration(bool complete){
    if (!lowerCalibrationDone){
        lowerCalibrationDone = false;
        calibrate_lower = 0;
        ref_mbar_lower = 0;
    } else {
        upperCalibrationDone = false;
        calibrate_upper = 0;
        ref_mbar_upper = 0;
    }
    avg_count = 0;
    average = 0;
    if (complete){
        lowerCalibrationDone = false;
        upperCalibrationDone = false;

        calibrationExceedMax = false;

        calibrate_lower = 0;
        calibrate_upper = 0;

        ref_mbar_lower = 0;
        ref_mbar_upper = 0;

        isRunning = false;
        userInputMarker = false;
    }
}

/** called after constructor, before showing page */
void CalibrationPage::initializePage() {
    //QString finishText = wizard()->buttonText(QWizard::FinishButton);


    sensor_iterator = new QMapIterator<int, BimosSensorPressure*>(config->sensorList);
    sensor = sensor_iterator->next().value();

    // no calibration running yet
    lowerCalibrationDone = false;
    upperCalibrationDone = false;
    calibrationExceedMax = false;
    calibrate_upper = 0;
    avg_count = 0;
    average = 0;
    ref_mbar_upper = 0;
    isRunning = false;
    userInputMarker = false;

    // user has not yet canceld any operation
    ok = true;


    // register first sensor and wait for singals (see newPressure)
    connect(sensor, SIGNAL(valueChanged(BimosSensorPressure*)), this, SLOT(newPressure(BimosSensorPressure*)));

    hasFinished = false;
    emit completeChanged();

}

bool CalibrationPage::isComplete() const {
    return hasFinished;
}

void CalibrationPage::cleanupPage(){
    qDebug() << "Aborted";
    abortCalibration(true);
    disconnect(connection);
}

void CalibrationPage::setTitleInfo(int sensorNumber, double targetValue, bool increase){
        setTitle(tr("Calibration of Sensor ") + QString::number(sensorNumber));
        setSubTitle(tr("Please ")
                    + (increase ? tr("INCREASE") : tr("DECREASE"))
                    + tr(" preassure to: ")
                    + QString::number(targetValue)
                    + tr("[mbar]")  );
}

void CalibrationPage::newPressure(BimosSensorPressure* sensor){
    //TODO we might need to check that the grabber timer is small enough to calibrate
    //TODO we might disconnect the graph from any event so the calibration is not plotted

    double value = sensor->getVolt();
    // TODO avoid redundancy
    if (!lowerCalibrationDone
            & ((value < LOWER_PRESSURE_THRESHOLD) || (value > (LOWER_PRESSURE_THRESHOLD + THRESHOLD_DELTA)) )){
        setProgressBarColor(value, false);
        abortCalibration(false);
        if (value < LOWER_PRESSURE_THRESHOLD){
            setTitleInfo(sensor->getOrdinal(), LOWER_PRESSURE_THRESHOLD, true);
        } else {
            setTitleInfo(sensor->getOrdinal(), LOWER_PRESSURE_THRESHOLD + THRESHOLD_DELTA, false);
        }
    } else if (lowerCalibrationDone & (value < UPPER_PRESSURE_THRESHOLD)) {
        setProgressBarColor(value, false);
        abortCalibration(false);
        setTitleInfo(sensor->getOrdinal(), UPPER_PRESSURE_THRESHOLD, true);
    } else {
        setProgressBarColor(value, true);
        if (isRunning){
            setSubTitle(tr("Please hold preassure!"));
            calibrationExceedMax = calibrate(sensor->getVolt());
            // caliration of lower value has finished -> ask user to input reference pressure
            if (userInputMarker){
                // temporaly disconnect sensor while showing dialog
                disconnect(sensor, 0, this, 0);
                // Dialog to ask for reference preassure lower value
                ref_mbar_lower = showAskRefMbarDialog(&ok);
                // reconnect sensor and start calibration of upper value
                if (ok){
                    connection = connect(sensor, SIGNAL(valueChanged(BimosSensor*)), this, SLOT(newPressure(BimosSensorPressure*)));
                } else {
                    abortCalibration(true);
                }
                // save actaual value to avoid NAN
                average = value;
                avg_count = 1;
                userInputMarker = false;
            // has the calibration finished (upper and! lower value)?
            } else if(upperCalibrationDone){
                disconnect(sensor, 0, this, 0);
                // Dialog to ask for reference preassure upper value
                ref_mbar_upper = showAskRefMbarDialog(&ok);
                if (ok){
                    // calculate calibration
                    calculateCalibration(sensor);
                    if (!sensor_iterator->hasNext()){
                        // all sensors have been calibrated
                        hasFinished = true;
                        setTitle(tr("Calibration finished."));
                        setSubTitle("");
                        emit completeChanged();
                    } else {
                        // get next sensor and start its calibration.
                        sensor = sensor_iterator->next().value();
                        // reset everything for the next sensor
                        abortCalibration(true);
                       connection =  connect(sensor, SIGNAL(valueChanged(BimosSensor*)), this, SLOT(newPressure(BimosSensorPressure*)));
                    }

                } else {
                   abortCalibration(true);
                }
            }
        } else {
            // nothing has been calibrated
            lowerCalibrationDone = false;
            upperCalibrationDone = false;
            calibrationExceedMax = false;

            // clear calibration parameter
            calibrate_lower = 0;
            calibrate_upper = 0;

            ref_mbar_lower = 0;
            ref_mbar_upper = 0;

            userInputMarker = false;

            // and start calibration
            isRunning = true;

            // preset average and count to avoid devision by zero!
            average = value;
            avg_count++;
        }
    }
}

void CalibrationPage::setProgressBarColor(float value, bool isValid) {
    if(isValid){
        pBar->setStyleSheet(pBar->property("defaultStyleSheet").toString() + " QProgressBar::chunk { background: green; }");
     } else {
        pBar->setStyleSheet(pBar->property("defaultStyleSheet").toString() + " QProgressBar::chunk { background: red; }");

    }
    // scale to 500 max for 5 bar
    pBar->setValue(value*100);
    pBar->setFormat("Volt value: " + QString::number(value) + " V, estimated pressure: " + QString::number(0.47762*(value)+1.2957) + "bar");
    pBar->repaint();
}


/** Setter config */
void CalibrationPage::setConfig(Config *conf) {
    config = conf;
}



